package hujz.java.file_server.http;

import hujz.java.file_server.IOathInterceptor;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static hujz.java.file_server.Configuration.INSTANCE;
import static io.netty.handler.codec.http.HttpResponseStatus.*;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_0;

public class DispatcherHandler extends SimpleChannelInboundHandler<HttpObject> {

    private static final String ServerPath = INSTANCE.getPath();
    private static final Logger LOGGER = LoggerFactory.getLogger(DispatcherHandler.class);

    private static IOathInterceptor interceptor = INSTANCE.getOathInterceptor();

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.channel().write(new DefaultHttpResponse(HTTP_1_0, INTERNAL_SERVER_ERROR)).addListener(ChannelFutureListener.CLOSE);
        ctx.channel().flush();
        ctx.channel().close();
    }

    private HttpResponse setACLResponseHeader(HttpRequest request, HttpResponse response) {
        String origin = request.headers().get("Origin");
        String acl_header = request.headers().get("Access-Control-Request-Headers");
        String acl_method = request.headers().get("Access-Control-Request-Method");

        response.headers().add("Access-Control-Allow-Credentials", "true");
        if (origin != null) {
            response.headers().add("Access-Control-Allow-Origin", origin);
        }
        if (acl_method != null) {
            response.headers().add("Access-Control-Allow-Methods", acl_method);
        } else {
            response.headers().add("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, OPTIONS");
        }
        if (acl_header != null) {
            response.headers().add("Access-Control-Allow-Headers", acl_header);
        } else {
            response.headers().add("Access-Control-Allow-Headers", "Content-Type,*");
        }
        return response;
    }

    private void writeResponse(ChannelHandlerContext ctx, HttpResponseStatus status) {
        ctx.channel().write(new DefaultHttpResponse(HttpVersion.HTTP_1_0, status)).addListener(ChannelFutureListener.CLOSE);
        ctx.channel().flush();
        ctx.channel().close();
    }

    private void writeResponse(ChannelHandlerContext ctx, HttpResponse response) {
        ctx.channel().write(response).addListener(ChannelFutureListener.CLOSE);
        ctx.channel().flush();
        ctx.channel().close();
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {

        ctx.channel().pipeline().remove(DispatcherHandler.class);
        if (msg instanceof HttpRequest) {
            HttpRequest request = (HttpRequest) msg;
            LOGGER.debug("[FileServer] --> accept request: " + ((HttpRequest) msg).uri());
            if (!interceptor.intercept(ctx, request)) {
                writeResponse(ctx, UNAUTHORIZED);
                return;
            }

            if (request.method() == HttpMethod.OPTIONS) {
                ctx.channel().writeAndFlush(setACLResponseHeader(request, new DefaultHttpResponse(HTTP_1_0, OK)));
                ctx.channel().flush();
                ctx.channel().close();
                return;
            }

            try {
                HttpResponse httpResponse = new DefaultHttpResponse(HTTP_1_0, OK);
                setACLResponseHeader(request, httpResponse);
                if ((request).uri().startsWith(ServerPath + "/getFile")) {
                    ctx.channel().pipeline().addLast(new HttpStaticFileServerHandler(ctx, request, httpResponse));
                } else if ((request).uri().startsWith(ServerPath + "/putFile")) {
                    ctx.channel().pipeline().addLast(new HttpUploadServerHandler(request, httpResponse));
                } else if ((request).uri().startsWith(ServerPath + "/image")) {
                    ctx.channel().pipeline().addLast(new HttpImageServerHandler(request, httpResponse));
                } else {
                    httpResponse.setStatus(NOT_FOUND);
                    writeResponse(ctx, httpResponse);
                    LOGGER.debug("[FileServer] <-- Not found uri \"{}\"", (request).uri());
                    return;
                }
            } catch (Exception e) {
                LOGGER.error("[FileServer] <-- Exception uri \"{}\"", (request).uri());
                LOGGER.error(e.getMessage(), e);
                writeResponse(ctx, INTERNAL_SERVER_ERROR);
                return;
            }

        }
    }
}
